home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************************/
- /* SORT.C - Functions related to the SORT command. */
- /***********************************************************************/
- /*
- * THE - The Hessling Editor. A text editor similar to VM/CMS xedit.
- * Copyright (C) 1991-1995 Mark Hessling
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to:
- *
- * The Free Software Foundation, Inc.
- * 675 Mass Ave,
- * Cambridge, MA 02139 USA.
- *
- *
- * If you make modifications to this software that you feel increases
- * it usefulness for the rest of the community, please email the
- * changes, enhancements, bug fixes as well as any and all ideas to me.
- * This software is going to be maintained and enhanced as deemed
- * necessary by the community.
- *
- * Mark Hessling email: M.Hessling@gu.edu.au
- * 36 David Road Phone: +61 7 849 7731
- * Holland Park Fax: +61 7 875 5314
- * QLD 4121
- * Australia
- */
-
- /*
- $Id: SORT.C 2.0 1995/01/26 16:34:40 MH Release MH $
- */
-
- #include <stdio.h>
-
- #include "the.h"
- #include "proto.h"
-
- #define MAX_SORT_FIELDS 10
-
- #define SF_ERROR 0
- #define SF_START 1
- #define SF_ORDER 2
- #define SF_LEFT 3
-
- struct sort_field
- {
- CHARTYPE order; /* A - ascending, D - descending */
- LENGTHTYPE left_col; /* left column */
- LENGTHTYPE right_col; /* right column */
- };
- typedef struct sort_field SORT_FIELD;
-
- CHARTYPE *sort_field_1;
- CHARTYPE *sort_field_2;
-
- SORT_FIELD sort_fields[MAX_SORT_FIELDS];
-
- short num_fields;
-
- #ifdef __STDC__
- static int cmp(const void *,const void *);
- #else
- static int cmp();
- #endif
-
- /***********************************************************************/
- #ifdef __STDC__
- static int cmp(const void *first,const void *second)
- #else
- static int cmp(first,second)
- void *first,*second;
- #endif
- /***********************************************************************/
- {
- /*-------------------------- external data ----------------------------*/
- /*--------------------------- local data ------------------------------*/
- register short i=0,j=0;
- short len=0,rc=RC_OK;
- LENGTHTYPE right_col=0,left_col=0;
- LINE *one = *(LINE **)first;
- LINE *two = *(LINE **)second;
- /*--------------------------- processing ------------------------------*/
- /*---------------------------------------------------------------------*/
- /* For each sort field defined in the array sort_fields, compare the */
- /* value of both lines for the specified column range. */
- /*---------------------------------------------------------------------*/
- for (i=0;i<num_fields;i++)
- {
- /*---------------------------------------------------------------------*/
- /* Calculate the length of the sort field. */
- /*---------------------------------------------------------------------*/
- len = sort_fields[i].right_col - sort_fields[i].left_col + 1;
- /*---------------------------------------------------------------------*/
- /* Set the two temporary fields to blanks. */
- /*---------------------------------------------------------------------*/
- memset(sort_field_1,' ',len);
- memset(sort_field_2,' ',len);
- /*---------------------------------------------------------------------*/
- /* For the first line to be compared, extract the portion of the line */
- /* that corresponds with the current sort column... */
- /*---------------------------------------------------------------------*/
- right_col = min(sort_fields[i].right_col,one->length);
- left_col = min(sort_fields[i].left_col,one->length);
- /*---------------------------------------------------------------------*/
- /* If the sort column lies after the end of the line, leave the */
- /* contents of the sort field blank. */
- /*---------------------------------------------------------------------*/
- if (sort_fields[i].left_col <= one->length)
- memcpy(sort_field_1,one->line+left_col-1,right_col-left_col+1);
- /*---------------------------------------------------------------------*/
- /* For the next line to be compared, extract the portion of the line */
- /* that corresponds with the current sort column... */
- /*---------------------------------------------------------------------*/
- right_col = min(sort_fields[i].right_col,two->length);
- left_col = min(sort_fields[i].left_col,two->length);
- /*---------------------------------------------------------------------*/
- /* If the sort column lies after the end of the line, leave the */
- /* contents of the sort field blank. */
- /*---------------------------------------------------------------------*/
- if (sort_fields[i].left_col <= two->length)
- memcpy(sort_field_2,two->line+left_col-1,right_col-left_col+1);
- /*---------------------------------------------------------------------*/
- /* If CASE IGNORE is on for the current view, set both sort fields to */
- /* uppercase for the comparison. */
- /*---------------------------------------------------------------------*/
- if (CURRENT_VIEW->case_sort == CASE_IGNORE)
- {
- for (j=0;j<len;j++)
- {
- if (islower(sort_field_1[j]))
- sort_field_1[j] = toupper(sort_field_1[j]);
- if (islower(sort_field_2[j]))
- sort_field_2[j] = toupper(sort_field_2[j]);
- }
- }
- /*---------------------------------------------------------------------*/
- /* If the two sort fields are equal, continue the sort with the next */
- /* sort field value. If the sort fields are different, return with the */
- /* the comparison value (if ASCENDING) or the comparison value negated */
- /* (if DESCENDING). */
- /*---------------------------------------------------------------------*/
- if ((rc = strncmp(sort_field_1,sort_field_2,len)) != 0)
- return((sort_fields[i].order == 'A') ? rc : -rc);
- }
- /*---------------------------------------------------------------------*/
- /* To get to here, the result of sorting on all fields has resulted in */
- /* both lines being equal. Return with 0 to indicate this. */
- /*---------------------------------------------------------------------*/
- return(0);
- }
- /***********************************************************************/
- #ifdef PROTO
- short execute_sort(CHARTYPE *params)
- #else
- short execute_sort(params)
- #endif
- /***********************************************************************/
- {
- /*-------------------------- external data ----------------------------*/
- extern bool in_profile;
- extern CHARTYPE *temp_cmd;
- extern VIEW_DETAILS *vd_mark;
- /*--------------------------- local data ------------------------------*/
- #define SOR_PARAMS 32
- register int i=0;
- CHARTYPE *word[SOR_PARAMS+1];
- unsigned short num_params=0;
- LINE **lfirst=NULL,**lp=NULL,*curr=NULL,*first=NULL,*last=NULL;
- LINETYPE num_lines=0L,j=0L,true_line=0L,dest_line=0L;
- short rc=RC_OK,direction=DIRECTION_FORWARD;
- LENGTHTYPE left_col=0,right_col=0,max_column_width=0;
- short state=0;
- CHARTYPE order='A';
- TARGET target;
- short target_type=TARGET_NORMAL|TARGET_BLOCK_CURRENT|TARGET_ALL|TARGET_SPARE;
- bool save_scope_all=FALSE;
- /*--------------------------- processing ------------------------------*/
- #ifdef TRACE
- trace_function("sort.c: execute_sort");
- #endif
- /*---------------------------------------------------------------------*/
- /* Save the current setting of scope_all to fool the validate_target() */
- /* routine into thinking scope_all is in effect. */
- /*---------------------------------------------------------------------*/
- save_scope_all = CURRENT_VIEW->scope_all;
- /*---------------------------------------------------------------------*/
- /* Validate first argument as a target... */
- /*---------------------------------------------------------------------*/
- initialise_target(&target);
- rc = validate_target(params,&target,target_type,get_true_line(),TRUE,TRUE);
- /*---------------------------------------------------------------------*/
- /* Restore the setting of scope_all before anything else is done. */
- /*---------------------------------------------------------------------*/
- CURRENT_VIEW->scope_all = save_scope_all;
- if (rc != RC_OK)
- {
- free_target(&target);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_INVALID_OPERAND);
- }
- true_line = target.true_line;
- num_lines = target.num_lines;
- dest_line = target.true_line + target.num_lines -1L;
- direction = DIRECTION_FORWARD;
- if (num_lines > 0L
- && true_line == 0L)
- {
- true_line++;
- num_lines--;
- }
- if (num_lines < 0L
- && true_line == CURRENT_FILE->number_lines+1L)
- {
- true_line--;
- num_lines++;
- }
- if (num_lines < 0L)
- {
- dest_line = target.true_line + target.num_lines +1L;
- true_line = true_line + num_lines + 1L;
- num_lines = num_lines * (-1L);
- direction = DIRECTION_BACKWARD;
- }
- /*---------------------------------------------------------------------*/
- /* Don't need to do anything if < 2 lines to be sorted. */
- /*---------------------------------------------------------------------*/
- if (num_lines < 2L)
- {
- free_target(&target);
- display_error(55,(CHARTYPE *)"",FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_OK);
- }
- /*---------------------------------------------------------------------*/
- /* Parse the remainder of the arguments and set up the sort_fields[] */
- /* array with valid values. */
- /*---------------------------------------------------------------------*/
- if (target.spare != (-1))
- num_params = param_split(strtrunc(target.rt[target.spare].string),word,SOR_PARAMS,WORD_DELIMS,TEMP_PARAM);
- /*---------------------------------------------------------------------*/
- /* Process parameters differently, depending on the number... */
- /* 0 parameter (target only) - if BOX BLOCK, then the sort field will */
- /* be the block columns, otherwise ZONE */
- /* settings will be used. */
- /* 1 parameters (target & order) - same as above but also validate the */
- /* ordering value. */
- /* > 1 parameters (target & sort fields) - validate each parameter. */
- /*---------------------------------------------------------------------*/
- switch(num_params)
- {
- case 0:
- case 1:
- sort_fields[0].left_col = CURRENT_VIEW->zone_start;
- sort_fields[0].right_col = CURRENT_VIEW->zone_end;
- sort_fields[0].order = order;
- num_fields = 1;
- if (target.rt[0].target_type == TARGET_BLOCK_CURRENT
- && MARK_VIEW->mark_type == M_BOX)
- {
- sort_fields[0].left_col = MARK_VIEW->mark_start_col;
- sort_fields[0].right_col = MARK_VIEW->mark_end_col;
- }
- /*---------------------------------------------------------------------*/
- /* No more processing if only 1 parameter. */
- /*---------------------------------------------------------------------*/
- if (num_params == 0)
- break;
- /*---------------------------------------------------------------------*/
- /* Processing for 2 parameters; validate ordering value. */
- /*---------------------------------------------------------------------*/
- if (equal((CHARTYPE *)"ascending",word[0],1)
- || equal((CHARTYPE *)"descending",word[0],1))
- {
- order = word[0][0];
- if (islower(order))
- order = toupper(order);
- sort_fields[0].order = order;
- break;
- }
- /*---------------------------------------------------------------------*/
- /* If the parameter is not Ascending or Descending, display error. */
- /*---------------------------------------------------------------------*/
- display_error(1,(CHARTYPE *)word[0],FALSE);
- free_target(&target);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_INVALID_OPERAND);
- break;
- /*---------------------------------------------------------------------*/
- /* More than 1 parameters; sort field(s) are being specified. */
- /*---------------------------------------------------------------------*/
- default:
- i = 0;
- num_fields = 0;
- state = SF_START;
- while(1)
- {
- switch(state)
- {
- case SF_START:
- if (equal((CHARTYPE *)"ascending",word[i],1)
- || equal((CHARTYPE *)"descending",word[i],1))
- {
- order = word[i][0];
- if (islower(order))
- order = toupper(order);
- sort_fields[num_fields].order = order;
- state = SF_ORDER;
- i++;
- break;
- }
- left_col = atoi(word[i]);
- if (left_col == 0)
- {
- state = SF_ERROR;
- break;
- }
- sort_fields[num_fields].order = order;
- sort_fields[num_fields].left_col = left_col;
- state = SF_LEFT;
- i++;
- break;
- case SF_ORDER:
- left_col = atoi(word[i]);
- if (left_col < 1)
- {
- state = SF_ERROR;
- break;
- }
- sort_fields[num_fields].left_col = left_col;
- state = SF_LEFT;
- i++;
- break;
- case SF_LEFT:
- right_col = atoi(word[i]);
- if (right_col < 1)
- {
- state = SF_ERROR;
- break;
- }
- sort_fields[num_fields].right_col = right_col;
- if (right_col < left_col)
- {
- state = SF_ERROR;
- break;
- }
- state = SF_START;
- i++;
- num_fields++;
- break;
- default:
- state = SF_ERROR;
- break;
- }
- /*---------------------------------------------------------------------*/
- /* If we have an error, display a message... */
- /*---------------------------------------------------------------------*/
- if (state == SF_ERROR)
- {
- free_target(&target);
- display_error(1,(CHARTYPE *)word[i],FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_INVALID_OPERAND);
- }
- /*---------------------------------------------------------------------*/
- /* If we have run out of parameters... */
- /*---------------------------------------------------------------------*/
- if (i == num_params)
- {
- /*---------------------------------------------------------------------*/
- /* ...then if we have the correct number of parameters, OK. */
- /*---------------------------------------------------------------------*/
- if (state == SF_START)
- break;
- else
- /*---------------------------------------------------------------------*/
- /* ...otherwise display an error. */
- /*---------------------------------------------------------------------*/
- {
- free_target(&target);
- display_error(1,strtrunc(target.rt[target.spare].string),FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_INVALID_OPERAND);
- }
- }
- }
- break;
- }
- /*---------------------------------------------------------------------*/
- /* Determine the maximum length of a sort field. */
- /*---------------------------------------------------------------------*/
- for (i=0;i<num_fields;i++)
- max_column_width = max(max_column_width,
- sort_fields[i].right_col - sort_fields[i].left_col + 1);
- /*---------------------------------------------------------------------*/
- /* Allocate memory for each of the temporary sort fields to the length */
- /* of the maximum field width. */
- /*---------------------------------------------------------------------*/
- if ((sort_field_1 = (CHARTYPE *)(*the_malloc)(max_column_width)) == NULL)
- {
- free_target(&target);
- display_error(30,(CHARTYPE *)"",FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_OUT_OF_MEMORY);
- }
- if ((sort_field_2 = (CHARTYPE *)(*the_malloc)(max_column_width)) == NULL)
- {
- free_target(&target);
- display_error(30,(CHARTYPE *)"",FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_OUT_OF_MEMORY);
- }
- /*---------------------------------------------------------------------*/
- /* Assign the values of the newly allocated array to the LINE pointers */
- /* for the target lines. */
- /*---------------------------------------------------------------------*/
- first = curr = lll_find(CURRENT_FILE->first_line,true_line);
- #ifdef MSWIN
- wqsort(curr,(long)num_lines,(int)sizeof(LINE *),cmp);
- #else
- /*---------------------------------------------------------------------*/
- /* Allocate memory for num_lines of LINE pointers. */
- /*---------------------------------------------------------------------*/
- if ((lfirst = (LINE **)(*the_malloc)(num_lines*sizeof(LINE *))) == NULL)
- {
- free_target(&target);
- display_error(30,(CHARTYPE *)"",FALSE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_OUT_OF_MEMORY);
- }
- lp = lfirst;
- for (j=0L;j<num_lines;j++)
- {
- lp[j] = curr;
- curr = curr->next;
- }
- last = curr;
- /*---------------------------------------------------------------------*/
- /* Sort the target array... */
- /*---------------------------------------------------------------------*/
- qsort(lfirst,num_lines,sizeof(LINE *),cmp);
- /*---------------------------------------------------------------------*/
- /* Merge the sorted array pointers into the linked list... */
- /*---------------------------------------------------------------------*/
- lp = lfirst;
- first->prev->next = lp[0];
- for (j=0L;j<num_lines;j++)
- {
- if (j == 0L)
- {
- lp[0]->prev = first->prev;
- lp[0]->next = lp[1];
- }
- else
- if (j == num_lines - 1L)
- {
- lp[num_lines-1]->next = last;
- lp[num_lines-1]->prev = lp[num_lines-2L];
- last->prev = lp[num_lines-1L];
- }
- else
- {
- lp[j]->next = lp[j+1L];
- lp[j]->prev = lp[j-1L];
- }
- }
- #endif
- /*---------------------------------------------------------------------*/
- /* If STAY is OFF, change the current and focus lines by the number */
- /* of lines calculated from the target. */
- /*---------------------------------------------------------------------*/
- if (!CURRENT_VIEW->stay /* stay is off */
- && target.num_lines != 0L)
- CURRENT_VIEW->focus_line = CURRENT_VIEW->current_line = find_next_in_scope(NULL,dest_line,direction);
- /*---------------------------------------------------------------------*/
- /* Free up the memory used for the sort fields and the target array. */
- /*---------------------------------------------------------------------*/
- (*the_free)(sort_field_1);
- (*the_free)(sort_field_2);
- (*the_free)(lfirst);
- free_target(&target);
- /*---------------------------------------------------------------------*/
- /* Increment alteration count... */
- /*---------------------------------------------------------------------*/
- if ((rc = increment_alt(CURRENT_FILE)) != RC_OK)
- {
- #ifdef TRACE
- trace_return();
- #endif
- return(rc);
- }
- sprintf(temp_cmd,"%ld line(s) sorted",num_lines);
- display_error(0,temp_cmd,TRUE);
- #ifdef TRACE
- trace_return();
- #endif
- return(RC_OK);
- }
-